# ending:utf-8
"""
@Time:2025/6/19 23:32
@Author:luyinglin
@Email:2902801287@qq.com
@File:travel.py
"""
import jieba
import pandas as pd
import re
from collections import Counter
from pyecharts.charts import Line,Pie,Scatter,Bar,Map,Grid
from pyecharts.charts import WordCloud
from pyecharts import options as opts
from pyecharts.globals import ThemeType
from pyecharts.globals import SymbolType
from pyecharts.commons.utils import JsCode
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from math import pi

# 读取数据
df = pd.read_excel(r'城市景点信息表.xlsx')

# 去除销量为0的行数据
df = df[df['销量']!=0]

# 将星级缺失值用‘未知’填充
df['星级'].fillna('未知', inplace=True) # 星级缺失值较多，如果删除缺失值，比较影响数据，故用“未知”填充

# 将所有缺失值都用‘未知’填充
df.fillna('未知', inplace=True)

# 1 销量前20热门景点数据
# 线性渐变
color_js = """new echarts.graphic.LinearGradient(0, 0, 1, 0,
    [{offset: 0, color: '#009ad6'}, {offset: 1, color: '#ed1941'}], false)"""


sort_info = df.sort_values(by='销量', ascending=True)
b1 = (
    Bar()
    .add_xaxis(list(sort_info['名称'])[-20:])
    .add_yaxis('热门景点销量', sort_info['销量'].values.tolist()[-20:],itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_js)))
    .reversal_axis()
    .set_global_opts(
        title_opts=opts.TitleOpts(title='热门景点销量数据'),
        yaxis_opts=opts.AxisOpts(name='景点名称'),
        xaxis_opts=opts.AxisOpts(name='销量'),
        )
    .set_series_opts(label_opts=opts.LabelOpts(position="right"))

)
# 将图形整体右移
g1 = (
    Grid()
        .add(b1, grid_opts=opts.GridOpts(pos_left='20%', pos_right='5%'))
)

# 2 各城市旅游景点销量分布
# 中国省份和自治区的名称映射字典
dictcode = {'北京': '北京市',
 '天津': '天津市',
 '河北': '河北省',
 '山西': '山西省',
 '内蒙古': '内蒙古自治区',
 '辽宁': '辽宁省',
 '吉林': '吉林省',
 '黑龙江': '黑龙江省',
 '上海': '上海市',
 '江苏': '江苏省',
 '浙江': '浙江省',
 '安徽': '安徽省',
 '福建': '福建省',
 '江西': '江西省',
 '山东': '山东省',
 '河南': '河南省',
 '湖北': '湖北省',
 '湖南': '湖南省',
 '广东': '广东省',
 '广西': '广西壮族自治区',
 '海南': '海南省',
 '重庆': '重庆市',
 '四川': '四川省',
 '贵州': '贵州省',
 '云南': '云南省',
 '西藏': '西藏自治区',
 '陕西': '陕西省',
 '甘肃': '甘肃省',
 '青海': '青海省',
 '宁夏': '宁夏回族自治区',
 '新疆': '新疆维吾尔自治区',
'台湾':'台湾',
 '香港':'香港',
 '澳门':'澳门'}
df_tmp1 = df[['城市','销量']]
df_counts = df_tmp1.groupby('城市').sum()
m1 = (
        Map()
        .add('假期出行分布', [list(z) for z in zip([dictcode[x] for x in df_counts.index.values.tolist() ], df_counts.values.tolist())], 'china')
        .set_global_opts(
        title_opts=opts.TitleOpts(title='假期出行数据地图分布'),
        visualmap_opts=opts.VisualMapOpts(max_=100000, is_piecewise=False,range_color=["white", "#fa8072", "#ed1941"]),
        )
    )

# 3 各省市4A-5A景区数量柱状图
# 线性渐变
color_js = """new echarts.graphic.LinearGradient(0, 1, 0, 0,
    [{offset: 0, color: '#009ad6'}, {offset: 1, color: '#ed1941'}], false)"""

df_tmp2 = df[df['星级'].isin(['4A', '5A'])]
df_counts = df_tmp2.groupby('城市').count()['星级']
b2 = (
        Bar()
            .add_xaxis(df_counts.index.values.tolist())
            .add_yaxis('4A-5A景区数量', df_counts.values.tolist(),itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_js)))
            .set_global_opts(
            title_opts=opts.TitleOpts(title='各省市4A-5A景区数量'),
            datazoom_opts=[opts.DataZoomOpts(), opts.DataZoomOpts(type_='inside')],
        )
    )

# 4 各省市4A-5A景区数量玫瑰图
df0 = df_counts.copy()
df0.sort_values(ascending=False, inplace=True)
c1 = (
    Pie()
    .add('', [list(z) for z in zip(df0.index.values.tolist(), df0.values.tolist())],
         radius=['30%', '100%'],
         center=['50%', '60%'],
         rosetype='area',
         )
    .set_global_opts(title_opts=opts.TitleOpts(title='地区景点数量'),
                     legend_opts=opts.LegendOpts(is_show=False),
                     toolbox_opts=opts.ToolboxOpts())
    .set_series_opts(label_opts=opts.LabelOpts(is_show=True, position='inside', font_size=12,
                                               formatter='{b}: {c}', font_style='italic',
                                               font_weight='bold', font_family='Microsoft YaHei'
                                               ))
)
# 5 各省市4A-5A景区数量阴影散点图
item_style = {'normal': {'shadowColor': '#000000',
                         'shadowBlur': 20,
                         'shadowOffsetX':5,
                         'shadowOffsetY':15
                         }
              }
s1 = (
        Scatter()
        .add_xaxis(df_counts.index.values.tolist())
        .add_yaxis('4A-5A景区数量', df_counts.values.tolist(),symbol_size=50,itemstyle_opts=item_style)
        .set_global_opts(visualmap_opts=opts.VisualMapOpts(is_show=False,
                                              type_='size',
                                              range_size=[5,50]))
)

# 6 各省市4A-5A景区地图分布
df_tmp3 = df[df['星级'].isin(['4A', '5A'])]
df_counts = df_tmp3.groupby('城市').count()['星级']
m2 = (
    Map()
    .add('4A-5A景区分布', [list(z) for z in zip([dictcode[x] for x in df_counts.index.values.tolist() ], df_counts.values.tolist())], 'china')
    .set_global_opts(
    title_opts=opts.TitleOpts(title='地图数据分布'),
    visualmap_opts=opts.VisualMapOpts(max_=50, is_piecewise=True),
    )
)

# 7 门票价格区间占比玫瑰图
import pandas as pd
import numpy as np
from pyecharts import options as opts
from pyecharts.charts import Pie
from pyecharts.globals import ThemeType

# 价格区间划分
bins = [0, 50, 100, 150, 200, 300, 500, 1000, np.inf]
labels = [
    '0-50',
    '50-100',
    '100-150',
    '150-200',
    '200-300',
    '300-500',
    '500-1000',
    '1000以上'
]


# 处理函数
def optimize_price_ranges(df):
    df['价格'] = pd.to_numeric(df['价格'], errors='coerce')  # 将价格列转换为数值型数据
    df = df.dropna(subset=['价格'])  # 删除价格列中为 NaN 的行

    # 应用新区间
    df['价格区间'] = pd.cut(df['价格'], bins=bins, labels=labels, right=False)  # 将价格划分到上述定义的区间中，并为每个价格分配一个对应的区间标签
    return df.groupby('价格区间', observed=False).size()  # 按照价格区间进行分组，并统计每个区间内的商品数量


# 使用示例
df_price = optimize_price_ranges(df)

p1 = (
    Pie(init_opts=opts.InitOpts(
            width='800px', height='600px',
            )
       )
        .add(
        '',
        [list(z) for z in zip(df_price.index.tolist(), df_price.values.tolist())],
        radius=['20%', '60%'],
        center=['40%', '50%'],
        rosetype='radius',
        label_opts=opts.LabelOpts(is_show=True),
        )
        .set_global_opts(title_opts=opts.TitleOpts(title='门票价格占比',pos_left='33%',pos_top="5%"),
                        legend_opts=opts.LegendOpts(type_='scroll', pos_left="80%",pos_top="25%",orient="vertical")
                        )
        .set_series_opts(label_opts=opts.LabelOpts(formatter='{b}: {c} ({d}%)'),position='outside')
    )

# 8 门票价格区间数量散点图
color_js = """new echarts.graphic.RadialGradient(
                    0.5, 0.5, 1,
                    [{offset: 0,
                      color: '#009ad6'},
                     {offset: 1,
                      color: '#ed1941'}
                      ])"""

s2 = (
        Scatter()
        .add_xaxis(df_price.index.tolist())
        .add_yaxis('门票价格区间', df_price.values.tolist(),symbol_size=50,itemstyle_opts=opts.ItemStyleOpts(color=JsCode(color_js)))
        .set_global_opts(
            yaxis_opts=opts.AxisOpts(name='数量'),
            xaxis_opts=opts.AxisOpts(name='价格区间(元)'))
        .set_global_opts(visualmap_opts=opts.VisualMapOpts(is_show=False,
                                              # 设置通过图形大小来表现数据
                                              type_='size',
                                              # 图形大小映射范围
                                              range_size=[5,50]))
)

# 9 景点简介词云
# 读取并拼接所有简介
contents = "".join('%s' % i for i in df['简介'].values.tolist())

# 分词
contents_list = jieba.cut(contents)

# 统计词频
ac = Counter(contents_list)

# 加载停用词文件并清理
stopwords = []
with open(r'C:\Users\DELL\数据可视化\stopwords.txt', "r", encoding="utf-8") as f:
    data = f.read()  # 读取文件
    stopwords = [word.strip() for word in data.split('\n') if word.strip()]  # 去除空白字符

# 精确删除停用词
for stopword in stopwords:
    if stopword in ac:
        del ac[stopword]

# 生成词云
w1 = (
    WordCloud()
    .add("",
         ac.most_common(150),
         word_size_range=[5, 100],
         textstyle_opts=opts.TextStyleOpts(font_family="cursive"),
         shape='star')
    .set_global_opts(title_opts=opts.TitleOpts(title="景点简介词云"))
)

# 10 景点简介词云-自定义模板
def get_w2():
    w2 = (
        WordCloud()
        .add(
            "",
            ac.most_common(200),
            word_size_range=[5, 80],
            shape=SymbolType.DIAMOND,
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(title="自定义样式词云图"),
        )
    )
    return w2
w2 = get_w2()
w2.render_notebook()

# 11 可视化大屏渲染
rating_avg = float(df['评分'].mean())  # 评分的平均值
price_avg = float(df['价格'].mean())  # 价格的平均值
sales_sum = int(df['销量'].sum())  # 销量的总数

def big_title(title="主标题"):
    c = Pie(
        init_opts=opts.InitOpts(
            chart_id="big_title",  # 图表的ID
            bg_color=JsCode("""new echarts.graphic.LinearGradient(0, 1, 0, 0, [{
                        offset: 0,color: '#080b30'
                    },{offset: 1,color: 'rgba(0, 77, 167, 1)'}], false)""")
        )
    )
    c.set_global_opts(
        title_opts=opts.TitleOpts(
            title=title,  # 标题文本
            title_textstyle_opts=opts.TextStyleOpts(  # 标题文本样式
                font_size=36,  # 字体大小为36
                color='#FFFFFF',  # 字体颜色为白色
            ),
            pos_left='center',  # 标题水平居中
            pos_top='middle'  # 标题垂直居中
        )
    )
    return c
main_title=big_title(title='全国旅游景点数据可视化大屏')

chart_id_counter = 0
def big_data(title='主标题', subtitle='副标题'):
    main_font_size = 28  # 设置主标题的字体大小
    sub_font_size = min(main_font_size * 1.2, 48)  # 设置副标题的字体大小，副标题大约是主标题的1.2倍，最大为48
    global chart_id_counter
    chart_id_counter += 1  # 每次调用函数时递增计数器

    c = Pie(
        init_opts=opts.InitOpts(
            chart_id=f"big_data_{chart_id_counter}",  # 使用计数器生成唯一的 chart_id
            bg_color='#080b30',
            theme='dark',
            width='300px',
            height='300px',
        )
    )
    c.set_global_opts(
        title_opts=opts.TitleOpts(
            title=title,
            subtitle=subtitle,
            title_textstyle_opts=opts.TextStyleOpts(
                font_size=main_font_size,
                color='#FFFFFF',
            ),
            subtitle_textstyle_opts=opts.TextStyleOpts(
                font_size=sub_font_size,
                color='#FFFFFF',
            ),
            pos_left='center',
            pos_top='middle',
        )
    )
    return c

rating=big_data(title = rating_avg,subtitle = '评分均值')
price=big_data(title = price_avg,subtitle = '价格均值')
sales=big_data(title = sales_sum,subtitle = '销量')